package fr.emse.tatiana.replayable;

import java.io.File;
import java.util.List;
import java.util.Vector;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.Path;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.IPersistableElement;
import fr.emse.tatiana.TatianaConstants;
import fr.emse.tatiana.corpus.Corpus;
import fr.emse.tatiana.display.BadDisplayFile;
import fr.emse.tatiana.display.DisplayFileReader;
import fr.emse.tatiana.display.DisplayFileWriter;
import fr.emse.tatiana.display.DisplayTable;
import fr.emse.tatiana.display.ReplayableDisplayShower;
import fr.emse.tatiana.filter.FilterExecuter;
import fr.emse.tatiana.filter.FilterParameter;
import fr.emse.tatiana.replayable.analysis.IAnalysis;

public class ReplayableInput implements IPersistableElement, IReplayableSaver, IReplayableInput {

	// TODO z later compare with FileEditorInput source code (for persistance in particular)
	
	public final static String DISPLAY_TYPE = "fr.emse.tatiana.replayableinput";

	private File filterfile;
	private FilterExecuter filter;
	private Corpus corpus;
	private boolean saved = false;

	private String layoutname;
	private String displayfilepath;
	private IFile replayablefile;

	public ReplayableInput(String path, Corpus c) {
		this.filterfile = new File(path);
		this.corpus = c; // TODO this corpus is not updated if the corpus gets updated
	}

	public ReplayableInput(IFile file) throws BadDisplayFile {
		this(getFilterPath(file),new Corpus(file.getProject()));
		this.replayablefile = file;
		this.layoutname = getLayoutName(file);
		this.saved = true;
	}


	public boolean exists() {
		return filterfile.exists();
	}

	public void init() throws BadDisplayFile {
		initFilter();
	}
	
	public void initFilter() throws BadDisplayFile {
		this.filter = new FilterExecuter(filterfile.getAbsolutePath());
		if (layoutname==null && filter.getLayoutPath()!=null) { // there is a layout we don't know about yet
			layoutname = new File(filter.getLayoutPath()).getName();
		}
		if (replayablefile != null) {
			DisplayFileReader reader = new DisplayFileReader(replayablefile.getLocation().toString());
			if (!DISPLAY_TYPE.equals(reader.getType())) {
				throw new BadDisplayFile(BadDisplayFile.WRONGFILETYPE +" expected:"+DISPLAY_TYPE);
			}
			if(reader.selectFirstItem() ) {
				boolean hasinfo = reader.selectFirstInfo("param");
				for (FilterParameter param : filter.getParameters()) {
					if (hasinfo) {
						reader.updateInfoValue("String");
						param.setReturnvalue((String)reader.infoValue());
						hasinfo = reader.selectNextInfo("param");
					} else {
						return;
					}
				}
			}
			runFilter(false); 
		}
	}

	public boolean runFilter(boolean nocache) {
		if (!filter.execute(nocache)) {
			return false; // TODO z later fix this whole return mess
		}
		// potentially changing the value of the current path
		ReplayableModelFactory.removeFilteredModel(displayfilepath);
		displayfilepath = filter.getResultPath();
		return true;
	}

	public String getDisplayFilePath() {
		return displayfilepath;
	}

	public String getLayoutName() {
		return (layoutname==null) ? "" :layoutname;
	}

	public ImageDescriptor getImageDescriptor() {
		return null;
	}

	public String getName() {
		return replayablefile==null ? "Untitled" : replayablefile.getName();
	}

	public IPersistableElement getPersistable() {
		return this;
	}

	public String getToolTipText() {
		return getName();
	}

	public Object getAdapter(Class adapter) {
		if (adapter == File.class) {
			return filterfile;
		} else if (adapter == Corpus.class) {
			return corpus;
		}
		return null;
	}

	public String getFactoryId() {
		return ReplayableInputFactory.getFactoryId();
	}

	public void saveState(IMemento memento) {
		ReplayableInputFactory.saveState(memento, this);
	}

	public File getFile() {
		return filterfile;
	}

	public Corpus getCorpus() {
		return corpus;
	}

	public boolean isSaved() {
		return this.saved;
	}

	public Vector<FilterParameter> getParameters() {
		if (filter!=null) {
			return filter.getParameters();
		}
		return null;
	}

	
	/**
	 * Sets a parameter of the filter. Clients should not set the parameter directly
	 * as this would not update the issaved value. Clients may want to fire dirty
	 * property after calling this.
	 * 
	 * @param param the parameter to be set
	 * @param value the value it will take
	 */
	public boolean setParam(FilterParameter param, String value) {
		if (value!=null && value.equals(param.getValue()))
			return false;
		param.setReturnvalue(value);
		this.saved=false;
		return true;
	}

	private boolean saveToReplayableFile(IFile file) {
		if (displayfilepath == null) {
			return false;
		}
		DisplayFileWriter writer = new DisplayFileWriter(file.getLocation().toString(),DISPLAY_TYPE);
		writer.newItem();
		writer.newInfo("projectname", "", corpus.getProject().getName());
		IFile rfile = ResourcesPlugin.getWorkspace().getRoot().findFilesForLocation(new Path(filterfile.getAbsolutePath()))[0];
		writer.newInfo("filterpath", "", rfile.getFullPath().makeRelative().toString());
		if (layoutname!=null) writer.newInfo("layoutname", "", layoutname);
		for (FilterParameter param : filter.getParameters()) {
			if (param.getValue()!=null) {
				writer.newInfo("param","",param.getValue());
			} else {
				writer.newInfo("param", "", "");
			}
		}
		writer.close();
		saved = true;
		return true;
	}

	public void doSave() {
		saveToReplayableFile(this.replayablefile);
	}

	public void doSave(String replayablename) {
		IProject project = corpus.getProject();
		IFolder replayables = project.getFolder(TatianaConstants.REPLAYABLES_FOLDER_NAME);

		try {
			replayables.refreshLocal(IResource.DEPTH_INFINITE, null);
			if (!replayables.exists()) {
				replayables.create(false, true, null);
			}
		} catch (CoreException e) {
			// shouldn't happen
			e.printStackTrace();
		}

		IFile file = replayables.getFile(replayablename);
		if (saveToReplayableFile(file)) {
			if (!file.exists()) {
				try {
					file.refreshLocal(IResource.DEPTH_ZERO, null);
				} catch (CoreException e) {
					// won't happen
					e.printStackTrace();
				}
			}
			replayablefile = file;
		}
	}

	public IFile getReplayableFile() {
		return this.replayablefile;
	}

	protected static String getFilterPath(IFile file) throws BadDisplayFile {
		DisplayFileReader reader = new DisplayFileReader(file.getLocation().toString());
		if( !reader.selectFirstItem() ) {
			throw new BadDisplayFile(BadDisplayFile.EMPTY);
		} else {
			String path = reader.getStringValue("filterpath", "missing filter path");
			if (path.startsWith("/")) {
				return path;
			} else {
				return ResourcesPlugin.getWorkspace().getRoot().findMember(new Path(path)).getLocation().toString();
			}
		}
	}

	protected static String getLayoutName(IFile file) {
		try {
			DisplayFileReader reader = new DisplayFileReader(file.getLocation().toString());
			if(reader.selectFirstItem() ) {
				
				return reader.getStringValue("layoutname", "missing layout name");
				
			}
		} catch (BadDisplayFile e) {
			// doesn't matter, just return null
		}
		return null;
	}

	public boolean hasFile() {
		return (getReplayableFile()!=null);
	}

	/* (non-Javadoc)
	 * @see fr.emse.tatiana.replayable.IReplayableInput#getDisplayModel(java.util.List)
	 */
	public IReplayableModel getDisplayModel(List<IAnalysis> analyses) {
		IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(TatianaConstants.TATIANA_PROJECT_NAME);
		String displaylayoutfile = (layoutname==null) ? null : project.getFolder(TatianaConstants.LAYOUT_FOLDER_NAME).getFile(layoutname).getLocation().toString();
		String displayfile = getDisplayFilePath();
		if (displayfile == null) {
			return null;
		}
		
		return ReplayableModelFactory.getFilteredReplayableModel(displayfile, displaylayoutfile, analyses);
		
	}

	public IProject getProject() {
		return corpus.getProject();
	}

	public File getFilterfile() {
		return filterfile;
	}

	public void close() {
		ReplayableModelFactory.releaseFilteredModel(displayfilepath);
	}

}
